package com.shareach.dao;

import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.data.domain.Page;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.shareach.common.BaseQueryOrder;
import com.shareach.common.PageImplResult;
import com.shareach.common.PageRequestOrder;

/**
 * 
 * @author zuoshuaiok@gmail.com
 */

public abstract class MongoDBBasicDao<T> {
	private static String counterCollectionName = "CounterCollection";
	@Resource
	protected MongoTemplate mongoTemplate;

	/**
	 * 用给定的 collectionName 创建�?��集合
	 * 
	 * @param collectionName
	 *            集合�?
	 */
	public void createCollection(String collectionName) {
		mongoTemplate.createCollection(collectionName);
	}

	/**
	 * 判断 collectionName 是否存在
	 * 
	 * @param collectionName
	 * @return
	 */
	public boolean collectionExists(String collectionName) {

		return mongoTemplate.collectionExists(collectionName);

	}

	/**
	 * 删除指定�?collectionName
	 * 
	 * @param collectionName
	 */

	public void dropCollection(String collectionName) {
		mongoTemplate.dropCollection(collectionName);
	}

	/**
	 * 
	 * 
	 * @param criteria
	 * @param clazz
	 */

	public T findOne(Criteria criteria, Class<T> clazz) {

		return mongoTemplate.findOne(new Query(criteria), clazz);
	}

	/**
	 * 
	 * 
	 * @param query
	 * @param clazz
	 */

	public T findOne(Query query, Class<T> clazz) {

		return mongoTemplate.findOne(query, clazz);
	}

	/**
	 * 
	 * @param criteria
	 * @param clazz
	 * @return
	 */
	public List<T> findList(Criteria criteria, Class<T> clazz) {

		return mongoTemplate.find(new Query(criteria), clazz);
	}

	/**
	 * 
	 * 
	 * @param query
	 * @param clazz
	 * @return
	 */
	public List<T> findList(Query query, Class<T> clazz) {
		return mongoTemplate.find(query, clazz);
	}

	/**
	 * 
	 * @param query
	 * @param clazz
	 * @return
	 */
	public T findAndRemove(Query query, Class<T> clazz) {

		return mongoTemplate.findAndRemove(query, clazz);

	}

	/**
	 * 
	 * @param entity
	 * @param collectionName
	 */
	public void remove(T entity, String collectionName) {

		mongoTemplate.remove(entity, collectionName);
	}

	/**
	 * 
	 * @param criteria
	 * @param clazz
	 */
	public void remove(Criteria criteria, Class<T> clazz) {
		if(criteria==null){
			mongoTemplate.remove(new Query(), clazz);
		}else{
			mongoTemplate.remove(new Query(criteria), clazz);
		}
	}
	/**
	 * 
	 * @param criteria
	 * @param clazz
	 */
	public void remove(Criteria criteria, String collectionName) {
		if(criteria==null){
			mongoTemplate.remove(new Query(), collectionName);
		}else{
			mongoTemplate.remove(new Query(criteria), collectionName);
		}
	}

	/**
	 * 
	 * @param collectionName
	 * @param entity
	 */
	public void save(String collectionName, T entity) {
		mongoTemplate.save(entity, collectionName);
	}

	/**
	 * @param entity
	 */
	public void save(T entity) {
		mongoTemplate.save(entity);

	}

	/**
	 * @param query
	 * @param update
	 * @param entityClass
	 */
	public void update(Criteria criteria, Update update, Class<T> entityClass) {
		mongoTemplate.updateFirst(new Query(criteria), update, entityClass);
	}

	/**
	 * 
	 * @param query
	 * @param update
	 * @param entityClass
	 */
	public void update(Query query, Update update, Class<T> entityClass) {
		mongoTemplate.updateFirst(query, update, entityClass);
	}

	public void updateMulti(Criteria criteria, Update update, Class<T> entityClass) {
		mongoTemplate.updateMulti(new Query(criteria), update, entityClass);
	}
	public void unset(Class<T> entityClass, String... keys) {
		for(String key:keys){
			Update update = new Update().unset(key);
			mongoTemplate.updateMulti(new Query(), update, entityClass);
		}
	}

	public long count(Query query, Class<T> clazz) {
		return mongoTemplate.count(query, clazz);
	}

	/**
	 * 
	 * @param criteria
	 * @param clazz
	 */
	public long count(Criteria criteria, Class<T> clazz) {
		return mongoTemplate.count(new Query(criteria), clazz);
	}

	/**
	 * 
	 * @param inputCollectionName
	 * @param mapFunction
	 * @param reduceFunction
	 * @param mapReduceOptions
	 * @param clazz
	 */
	public MapReduceResults<T> mapReduce(String inputCollectionName, String mapFunction, String reduceFunction,
			MapReduceOptions mapReduceOptions, Class<T> clazz) {
		return mongoTemplate.mapReduce(inputCollectionName, mapFunction, reduceFunction, mapReduceOptions, clazz);
	}

	/**
	 * 
	 * @param criteria
	 *            查询条件
	 * @param inputCollectionName
	 *            集合
	 * @param groupBy
	 *            被统�?
	 * @param clazz
	 */
	public GroupByResults<T> group(Criteria criteria, String inputCollectionName, GroupBy groupBy, Class<T> clazz) {
		return mongoTemplate.group(criteria, inputCollectionName, groupBy, clazz);
	}

	/**
	 * 
	 * @param inputCollectionName
	 * @param groupBy
	 * @param clazz
	 */
	public GroupByResults<T> group(String inputCollectionName, GroupBy groupBy, Class<T> clazz) {
		return mongoTemplate.group(inputCollectionName, groupBy, clazz);
	}

	public synchronized long increaseAndReturn(String counterName) {
		long id = 0;
		try {
			id = increaseAndReturn(counterCollectionName, counterName);
		} catch (MongoException e) {
			e.printStackTrace();
			return 0;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
		return id;
	}

	public long increaseAndReturn(String collectionName, String key) throws MongoException, Exception {
		DBObject update = new BasicDBObject("$inc", new BasicDBObject("counter", 1));
		DBObject query = new BasicDBObject("_id", key);
		DBObject result = mongoTemplate.getCollection(collectionName).findAndModify(query, update);
		if (result == null) {
			DBObject doc = new BasicDBObject();
			doc.put("counter", 1L);
			doc.put("_id", key);
			// insert(collectionName, doc);
			mongoTemplate.save(doc, collectionName);
			return 1L;
		}
		return (Long) result.get("counter");
	}

	
	/**
	 * 计数器置为0
	 * @param counterName
	 */
	public void zeroCounter(String counterName){
		mongoTemplate.remove(new Query(Criteria.where("_id").is(counterName)), counterCollectionName);
	}
	
	public Page<T> getPageResult(Query query, BaseQueryOrder order, Class<T> clazz) {
		if (null == order) {
			order = new BaseQueryOrder();
		}

		if (order.getSort() != null && order.getSort().size() > 0) {
			for (Map.Entry<String, String> entry : order.getSort().entrySet()) {
				query.sort().on(entry.getKey(), Order.valueOf(entry.getValue()));
			}
		}
		PageRequestOrder request = new PageRequestOrder(Integer.parseInt(order.getNumber()), order.getPageSize());

		long totalSize = count(query, clazz);

		query.skip(request.start()).limit(order.getPageSize());

		List<T> ps = findList(query, clazz);

		Page<T> result = new PageImplResult<T>(ps, request, totalSize);

		return result;
	}
}
